package com.ruoyi.flowable.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.page.PageDomain;
import com.ruoyi.common.core.web.page.TableSupport;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.flowable.constant.ProcessConstants;
import com.ruoyi.flowable.domain.FlowableModel;
import com.ruoyi.flowable.domain.bo.FlowableMetaInfoBo;
import com.ruoyi.flowable.domain.bo.FlowableModelBo;
import com.ruoyi.flowable.enums.CacheType;
import com.ruoyi.flowable.enums.FormType;
import com.ruoyi.flowable.service.FlowServiceFactory;
import com.ruoyi.flowable.service.IFlowableCategoryService;
import com.ruoyi.flowable.service.IFlowableModelService;
import com.ruoyi.flowable.utils.ModelHelper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 流程模型Service业务层处理
 *
 * @author ruoyi
 * @createtime  2023-11-28
 */
@Service
@Slf4j
public class FlowableModelServiceImpl extends FlowServiceFactory implements IFlowableModelService
{

    @Autowired
    private IFlowableCategoryService flowableCategoryService;
    /**
     * 查询流程模型
     *
     * @param modelId 流程模型主键
     * @return 流程模型
     */
    @Override
    public FlowableModel selectFlowableModelByModelId(String modelId)
    {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (model==null) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        String bpmnXml ;
        try {
            bpmnXml = queryBpmnXmlById(modelId);
        } catch (UnsupportedEncodingException e) {
            //e.printStackTrace();
            log.error("模型转换为字符串失败！模型id:"+modelId,e);
            throw new RuntimeException("模型转换为字符串失败！");
        }
        FlowableModel modelVo = new FlowableModel();
        modelVo.setModelId(model.getId());
        modelVo.setModelName(model.getName());
        modelVo.setModelKey(model.getKey());
        modelVo.setCategory(model.getCategory());
        modelVo.setCreateTime(model.getCreateTime());
        modelVo.setVersion(model.getVersion());
        modelVo.setBpmnXml(bpmnXml);
        FlowableMetaInfoBo metaInfo = JSON.parseObject(model.getMetaInfo(), FlowableMetaInfoBo.class);
        if (metaInfo != null) {
            modelVo.setDescription(metaInfo.getDescription());
            modelVo.setFormType(metaInfo.getFormType());
            modelVo.setFormId(metaInfo.getFormId());
            if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) {
                //TODO
                //WfFormVo wfFormVo = formService.queryById(metaInfo.getFormId());
                //modelVo.setContent(wfFormVo.getContent());
            }
        }
        return modelVo;
    }

    /**
     * 查询流程模型列表
     *
     * @param flowableModel 流程模型
     * @return 流程模型
     */
    @Override
    public List<FlowableModel> selectFlowableModelList(FlowableModelBo flowableModel)
    {
        PageDomain pageDomain = TableSupport.getPageDomain();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();


        ModelQuery modelQuery = repositoryService.createModelQuery().modelTenantId(SecurityUtils.getEnterpriseCode()).latestVersion().orderByCreateTime().desc();


        // 构建查询条件
        if (StringUtils.isNotBlank(flowableModel.getModelKey())) {
            modelQuery.modelKey(flowableModel.getModelKey());
        }
        if (StringUtils.isNotBlank(flowableModel.getModelName())) {
            modelQuery.modelNameLike("%" + flowableModel.getModelName() + "%");
        }
        if (StringUtils.isNotBlank(flowableModel.getCategory())) {
            modelQuery.modelCategory(flowableModel.getCategory());
        }


        // 执行查询
        long pageTotal = modelQuery.count();
        if (pageTotal <= 0) {
            return new ArrayList<>();
        }
        //有bug 先注掉
        //modelQuery = modelQuery.latestVersion();
        int offset = pageSize * (pageNum - 1);
        List<Model> modelList = modelQuery.listPage(offset, pageSize);
        List<FlowableModel> flowableModelList = new ArrayList<>(modelList.size());
        //获取流程分类缓存
        Map<String,String> flowCategoryMap = SpringUtils.getBean(RedisService.class).getCacheObject(CacheType.FLOWCATEGORY.getCode());
        if(flowCategoryMap==null){
            flowCategoryMap = flowableCategoryService.updateRedis(null);
        }
        Map<String, String> finalFlowCategoryMap = flowCategoryMap;
        modelList.forEach(model -> {
            FlowableModel modelVo = new FlowableModel();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            //翻译分类
            modelVo.setCategory(finalFlowCategoryMap.get(model.getCategory()));
            modelVo.setCreateTime(model.getCreateTime());
            modelVo.setVersion(model.getVersion());
            FlowableMetaInfoBo metaInfo = JSON.parseObject(model.getMetaInfo(), FlowableMetaInfoBo.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            flowableModelList.add(modelVo);
        });
        return flowableModelList;
    }

    /**
     * 新增流程模型
     *
     * @param flowableModel 流程模型
     * @return
     */
    @Override
    public int insertFlowableModel(FlowableModel flowableModel)
    {
        flowableModel.setCreateTime(DateUtils.getNowDate());
        Model model = repositoryService.newModel();
        model.setName(flowableModel.getModelName());
        model.setKey(flowableModel.getModelKey());
        model.setCategory(flowableModel.getCategory());
        model.setTenantId(SecurityUtils.getEnterpriseCode());
        String metaInfo = buildMetaInfo(new FlowableMetaInfoBo(), flowableModel.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
        return 1;
    }

    /**
     * 修改流程模型
     *
     * @param flowableModel 流程模型
     * @return
     */
    @Override
    public int updateFlowableModel(FlowableModel flowableModel)
    {
        // 根据模型Key查询模型信息
        Model model = repositoryService.getModel(flowableModel.getModelId());
        if (model==null) {
            throw new RuntimeException("流程模型不存在！");
        }
        model.setCategory(flowableModel.getCategory());
        FlowableMetaInfoBo metaInfoDto = JSON.parseObject(model.getMetaInfo(), FlowableMetaInfoBo.class);
        String metaInfo = buildMetaInfo(metaInfoDto, flowableModel.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
        return 1;
    }

    /**
     * 批量删除流程模型
     *
     * @param modelIds 需要删除的流程模型主键
     * @return
     */
    @Override
    public int deleteFlowableModelByModelIds(String[] modelIds)
    {
        for(String id:modelIds){
            Model model = repositoryService.getModel(id);
            if (model==null) {
                throw new RuntimeException("流程模型不存在！");
            }
            repositoryService.deleteModel(id);
        }
        return modelIds.length;
    }

    /**
     * 删除流程模型信息
     *
     * @param modelId 流程模型主键
     */
    @Override
    public void deleteFlowableModelByModelId(String modelId)
    {
        Model model = repositoryService.getModel(modelId);
        if (model==null) {
            throw new RuntimeException("流程模型不存在！");
        }
        repositoryService.deleteModel(modelId);
    }

    /**
     * 部署流程
     *
     * @param modelId 模型id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deployModel(String modelId) throws UnsupportedEncodingException {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (model== null) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        String bpmnXml = queryBpmnXmlById(modelId);
        //BpmnModel bpmnModel = ModelHelper.getBpmnModel(bpmnXml);
        String processName = model.getName() + ProcessConstants.SUFFIX;
        Deployment deployment = repositoryService.createDeployment()
                .key(model.getKey())
                .name(model.getName())
                .tenantId(SecurityUtils.getEnterpriseCode())
                .category(model.getCategory())
                .addString(model.getKey() + ".bpmn20.xml", bpmnXml)
                .deploy();
        // 调整分类
        ProcessDefinition precessDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        repositoryService.setProcessDefinitionCategory(precessDefinition.getId(),model.getCategory());
       // TODO:保存部署表单

    }

    /**
     * 构建模型扩展信息
     * @return 返回模型扩展信息
     */
    private String buildMetaInfo(FlowableMetaInfoBo metaInfo, String description) {
        // 只有非空，才进行设置，避免更新时的覆盖
        if (StringUtils.isNotEmpty(description)) {
            metaInfo.setDescription(description);
        }
        if (StringUtils.isNotEmpty(metaInfo.getCreateUser())) {
            metaInfo.setCreateUser(SecurityUtils.getUsername());
        }
        return JSON.toJSONString(metaInfo);
    }

    /**
     * 将模型数据转字符串返回
     * @param modelId 模型id
     * @return 返回模型字符串格式
     */
    public String queryBpmnXmlById(String modelId) throws UnsupportedEncodingException {
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        if(bpmnBytes!=null) {
            return new String(bpmnBytes, StandardCharsets.UTF_8);
        }else{
            return new String("");
        }
    }

    /**
     * 查询模型历史版本
     *
     * @param modelBo
     * @return
     */
    @Override
    public List<FlowableModel> historyList(FlowableModelBo modelBo) {
        ModelQuery modelQuery = repositoryService.createModelQuery()
                .modelTenantId(SecurityUtils.getEnterpriseCode())
                .modelKey(modelBo.getModelKey())
                .orderByModelVersion()
                .desc();
        // 执行查询（不显示最新版，-1）
        long pageTotal = modelQuery.count() - 1;
        if (pageTotal <= 0) {
            return new ArrayList<>();
        }
        PageDomain pageDomain = TableSupport.getPageDomain();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        // offset+1，去掉最新版
        int offset = 1 + pageSize * (pageNum - 1);
        List<Model> modelList = modelQuery.listPage(offset, pageSize);
        List<FlowableModel> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            FlowableModel modelVo = new FlowableModel();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            modelVo.setCategory(model.getCategory());
            modelVo.setCreateTime(model.getCreateTime());
            modelVo.setVersion(model.getVersion());
            FlowableMetaInfoBo metaInfo = JSON.parseObject(model.getMetaInfo(), FlowableMetaInfoBo.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            modelVoList.add(modelVo);
        });
        return modelVoList;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveModel(FlowableModelBo modelBo) {
        // 查询模型信息
        Model model = repositoryService.getModel(modelBo.getModelId());
        if (model==null) {
            throw new RuntimeException("流程模型不存在！");
        }
        log.info("==========bpmnxml=========");
        log.info(modelBo.getBpmnXml());
        BpmnModel bpmnModel = ModelHelper.getBpmnModel(modelBo.getBpmnXml());
        if (bpmnModel==null) {
            throw new RuntimeException("获取模型设计失败！");
        }
        //String processName = bpmnModel.getMainProcess().getName();
        // 获取开始节点
        StartEvent startEvent = ModelHelper.getStartEvent(bpmnModel);
        if (startEvent==null) {
            throw new RuntimeException("开始节点不存在，请检查流程设计是否有误！");
        }
        // 获取开始节点配置的表单Key
//        if (StringUtils.isBlank(startEvent.getFormKey())) {
//            throw new RuntimeException("请配置流程表单");
//        }
        Model newModel;
        if (Boolean.TRUE.equals(modelBo.getNewVersion())) {
            newModel = repositoryService.newModel();
            newModel.setName(model.getName());
            newModel.setKey(model.getKey());
            newModel.setCategory(model.getCategory());
            newModel.setMetaInfo(model.getMetaInfo());
            //获取最大版本号
            ModelQuery modelQuery = repositoryService.createModelQuery()
                    .modelTenantId(SecurityUtils.getEnterpriseCode())
                    .modelName(model.getName())
                    .modelCategory(model.getCategory())
                    .orderByModelVersion()
                    .desc();
            int offset = 1 * (1 - 1);
            List<Model> modelList = modelQuery.listPage(offset, 1);
            newModel.setVersion(modelList.get(0).getVersion() + 1);
        } else {
            newModel = model;
            // 设置流程名称
            newModel.setName(model.getName());
        }
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        repositoryService.addModelEditorSource(newModel.getId(), ModelHelper.getBpmnXml(bpmnModel));
    }

    /**
     * 设置为最新版本
     *
     * @param modelId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void latestModel(String modelId) throws UnsupportedEncodingException {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (model==null) {
            throw new RuntimeException("流程模型不存在！");
        }
        String bpmnXml = queryBpmnXmlById(modelId);
        Integer latestVersion = repositoryService.createModelQuery()
                .modelTenantId(SecurityUtils.getEnterpriseCode())
                .modelKey(model.getKey())
                .latestVersion()
                .singleResult()
                .getVersion();
        if (model.getVersion().equals(latestVersion)) {
            throw new RuntimeException("当前版本已是最新版！");
        }
        Model newModel = repositoryService.newModel();
        newModel.setName(model.getName());
        newModel.setKey(model.getKey());
        newModel.setCategory(model.getCategory());
        newModel.setMetaInfo(model.getMetaInfo());
        newModel.setVersion(latestVersion + 1);
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        repositoryService.addModelEditorSource(newModel.getId(), bpmnXml.getBytes(StandardCharsets.UTF_8));
    }
}
